package cn.huaqingcheng.tianshu.infrastructure.sys.adapter;

import cn.huaqingcheng.tianshu.common.core.model.AttributeMap;
import cn.huaqingcheng.tianshu.common.core.model.TemporaryUrl;
import cn.huaqingcheng.tianshu.core.sys.adapter.FileInfo;
import cn.huaqingcheng.tianshu.core.sys.adapter.FileStorageService;
import cn.huaqingcheng.tianshu.core.sys.model.SysFileScheme;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Map;

/**
 * 本地文件存储服务
 */
@Component
@RequiredArgsConstructor
public class LocalFileStorageService implements FileStorageService {

    private final LocalFileStorageProperties properties;


    @Override
    public boolean support(SysFileScheme scheme) {
        return scheme == SysFileScheme.LOCAL_FILE;
    }

    @Override
    public FileInfo save(InputStream is, String path, AttributeMap<Object> attrs) {
        Path target = getTarget(path, path);

        if (Files.exists(target)) {
            throw new RuntimeException("文件已存在: " + path);
        }

        try {
            Files.createDirectories(target.getParent());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        long size;
        try (OutputStream os = Files.newOutputStream(target, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)) {
            size = is.transferTo(os);
            os.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        try {
            String type = Files.probeContentType(target);
            URI uri = SysFileScheme.LOCAL_FILE.customSchemeUrl("", path);

            return new FileInfo(type, size, uri);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public InputStream read(URI uri) {
        String path = uri.normalize().getPath();
        Path target = getTarget(path, uri);
        if (Files.notExists(target)) {
            throw new RuntimeException("文件不存在: " + uri);
        }
        if (!Files.isReadable(target)) {
            throw new RuntimeException("文件不可读: " + uri);
        }

        try {
            return Files.newInputStream(target, StandardOpenOption.READ);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public TemporaryUrl temporaryUrl(URI uri) {
        return new TemporaryUrl(
                "GET",
                "/file/" + uri.getPath(),
                Map.of()
        );
    }

    private @NotNull Path getTarget(String path, Object msg) {
        Path root = properties.getRoot();
        Path target = Path.of(root + path).normalize();

        if (!target.toAbsolutePath().startsWith(root)) {
            throw new RuntimeException("文件路径非法: " + msg);
        }
        return target;
    }

}
